#ifndef _PARTICLE_SYSTEM_H_
#define _PARTICLE_SYSTEM_H_

#include <stdlib.h>

#include "..\glew\glew.h"
#include "..\glut\glut.h"

#include "..\cuCommon\cuTypes.h"
#include "..\cuCommon\cuMath.h"

#include "..\glCommon\glLightMap.h"
#include "..\glCommon\glTexture.h"
#include "..\glCommon\glShader.h"
#include "..\glCommon\glProgram.h"
#include "..\glCommon\glFramebuffer.h"

#include "Random.h"
#include "ImprovedPerlinNoise.h"
#include "Camera.h"

#include "..\glCommon\glRender.h"

#include "..\cuCommon\cuBuffer.h"
#include "..\RadixSort\IceRevisitedRadix.h"

// particle system
class ParticleSystem 
{
public:
    int m_size;
    int m_numActive;

    float4 m_Scale;
    float4 m_Position;
    float4 m_Velocity;

    // Particle Buffers
    sf4Buffer m_Pos;
    sf4Buffer m_Vel;
    sf4Buffer m_Color;
    sf_Buffer  m_Zs;

    int m_emitIndex;

    float m_pointRadius;
    float m_pointScale;

    int m_numSlices;
    int m_batchSize;
    int m_sliceNo;

    float m_shadowAlpha;
    float m_spriteAlpha;

    float3 m_lightDir, m_viewDir;
    float4 m_halfDir, m_halfDirES;
    bool   m_invView;

    // viewer view and projection, light view and projection, shadow matrix
    matrix4x4 m_vvMatrix, m_vpMatrix, m_lvMatrix, m_lpMatrix, m_sMatrix; 

    CglProgram m_particleProg, m_shadowProg;

    glLightMap * m_pShadow, * m_pHalfRes;

    C_Camera * m_pvCamera, * m_plCamera;

    RadixSort sorter;

public:
    ParticleSystem(int size);
    ~ParticleSystem();

    void InitGrid(int3 dim, float r, bool bCUDA = false);
    void InitSphere(int3 dim, float r, bool bCUDA = false);

    void AddNoiseVelocity(float freq, float scale, float3 offset, int sett, bool bCUDA = false);
    void AddNoisePosition(float freq, float scale, bool bCUDA = false);

    void ComputeHalf();

    void Reset();
    void Emit(float speed = 0.1, bool bCUDA = false);
    void Animate(bool bCUDA = false);

    void SetOffscreenBufferPtr(glLightMap * pShadow, glLightMap * pHalfRes);
    void SetCameraPtr(C_Camera * pvCamera, C_Camera * plCamera);

    void glRenderPoints(int start, int count);
    void glRender(C_Camera * pvCamera, C_Camera * plCamera);
    void glRender();

    void glRenderSprites(CglProgram * pProg, int start, int count, float alpha, float fov);

    void Composite(uint32 w, uint32 h, CglProgram * pDisplay);

    void Sync(C_Camera *pCamera);

protected:
    void SortByHalfDir();
    void glRenderBatchs();
    void glRenderLight(int i);
    void glRenderView(int i);
    void glRenderSorted(int start, int count, float alpha);
};


#endif